package org.ust.extraction;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodNode;

public class Analyzer
{
	private static final String	STATIC_FIELD		= "PUTSTATIC|GETSTATIC";
	private final String		PARAM_TYPE_DESC		= "((Z|C|B|S|I|F|J|D)|(\\[*((Z|C|B|S|I|F|J|D)|"
															+ "((L[a-z]+\\/[a-z]+\\/[A-Z]+[a-z]*;))))|"
															+ "(L[a-z]+\\/[a-z]+\\/[A-Z]+[a-z]*;))";
	private final String		RETURN_TYPE_DESC	= "((Z|C|B|V|S|I|F|J|D)|(\\[*((Z|V|C|B|S|I|F|J|D)|"
															+ "((L[a-z]+\\/[a-z]+\\/[A-Z]+[a-z]*;))))|"
															+ "(L[a-z]+\\/[a-z]+\\/[A-Z]+[a-z]*;))";
	private final String		FUNCTION_NAME		= "^[a-zA-Z][a-zA-Z0-9]*";
	private final String		OPEN_BRACKET		= "\\(";
	private final String		CLOSE_BRACKET		= "\\)";
	private final String		ID_NAME				= "[_a-zA-Z]+[_a-zA-Z0-9]*";
	private final String		PACKAGE_NAME		= "^[a-zA-Z]+[a-zA-Z0-9]*\\/([a-zA-Z]+[a-zA-Z0-9]*\\/)*";
	private final String		CONSTRUCTOR_NAME	= "<init>";

	public Analyzer ( File classFile ) throws IOException
	{
		// System.out.println ( "Analysing..." );
		// ClassReader cr = new ClassReader ( new FileInputStream ( classFile )
		// );
		// ClassNode classNode = new ClassNode ( );
		// cr.accept ( classNode , 0 );
		// List < MethodNode > methods = classNode.methods;
		// for ( MethodNode methodNode : methods )
		// {
		// System.out.println ( "Method Name:" + methodNode.access + " "
		// + methodNode.name + " " + methodNode.desc );
		//
		// InsnList insns = methodNode.instructions;
		// Iterator i = insns.iterator ( );
		// while ( i.hasNext ( ) )
		// {
		// AbstractInsnNode in = ( AbstractInsnNode ) i.next ( );
		//				
		// }
		//
		// }

		// System.out.println ( "Done" );

		// StringWriter strWriter = new StringWriter ( );
		// TraceClassVisitor tcv = new TraceClassVisitor ( new PrintWriter (
		// strWriter ) );
		// ClassReader cr = new ClassReader ( new FileInputStream ( classFile )
		// );
		// cr.accept ( tcv , 0 );
		//
		// StringBuffer classBuffer = strWriter.getBuffer ( );
		// System.out.println ( classBuffer );

		// if ( strWriter != null )
		// {
		// strWriter.close ( );
		// }
	}

	private void parse ( File classFile )
	{

	}

	private void analyseClassBuffer_org ( StringBuffer classBuffer )
	{
		StringTokenizer st = new StringTokenizer ( classBuffer.toString ( ) ,
				"\n" );
		while ( st.hasMoreTokens ( ) )
		{
			String line = st.nextToken ( );
			ArrayList < String > words = null;
			st = new StringTokenizer ( line , " " );
			words = new ArrayList < String > ( st.countTokens ( ) );
			fillup ( words , st );

			try
			{
				if ( isClassDeclaration ( words ) )
				{
					System.out
							.println ( "Analysing Class Declaration: " + line );
					samplingClassDeclaration ( words );
				}
				else if ( isMemberPublicFunction ( words ) )
				{
					// System.out.println ( "Analysing Public Function: " + line
					// );
					// samplingPublicFunction ( words );
				}
				else if ( isMemberPublicField ( words ) )
				{
					// System.out.println ( "Analysing Public Field: " + line );
					// samplingPublicField ( words );
				}
				else if ( isMemberConstructor ( words ) )
				{
					// System.out.println ( "Analysing Constructor: " + line );
					// samplingConstructor ( words );
				}
				else if ( isStaticFunction ( words ) )
				{
					// System.out.println ( "Analysing Static Function: " + line
					// );
					// samplingStaticFunction ( words );
				}
				else if ( isStaticField ( words ) )
				{
					// System.out.println ( "Analysing Static Field: " + line );
					// samplingStaticFunction ( words );
				}
				// else if ( isInvokingPublicFunction ( words ) )
				// {
				// System.out.println ( "Analysing Invoking Public Function: "
				// + line );
				// samplingInvokingPublicFunction ( words );
				// }
				// else if ( isInvokingPublicField ( words ) )
				// {
				// System.out.println ( "Analysing Invoking Public Field: "
				// + line );
				// samplingInvokingPublicFunction ( words );
				// }
				// else if ( isInvokingConstructor ( words ) )
				// {
				// System.out.println ( "Analysing Invoking Constructor: "
				// + line );
				// samplingInvokingConstructor ( words );
				// }
				// else if ( isInvokingStaticFunction ( words ) )
				// {
				// System.out.println ( "Analysing Invoking Static Function: "
				// + line );
				// samplingInvokingStaticFunction ( words );
				// }
			}
			catch ( WordListInitException e )
			{
				System.out.println ( e.getMessage ( ) + " in '" + line + "'" );
				e.printStackTrace ( );
			}
		}
	}

	private void samplingClassDeclaration ( ArrayList < String > words )
	{
		Class classDeclaration = new Class ( );
		classDeclaration.setName ( words.get ( 2 ) );
	}

	private boolean isClassDeclaration ( ArrayList < String > words )
			throws WordListInitException
	{
		if ( words == null )
		{
			throw new WordListInitException (
					"Word list cannot be initialized." );
		}
		if ( words.get ( 0 ).equals ( "public" )
				&& "class".equals ( words.get ( 1 ) ) )
		{
			return true;
		}

		return false;
	}

	private void flipIndicator ( boolean insideFunctionIndicator )
	{
		if ( insideFunctionIndicator )
		{
			insideFunctionIndicator = false;
		}
		else
		{
			insideFunctionIndicator = true;
		}
	}

	private boolean isStaticField ( ArrayList < String > words )
			throws WordListInitException
	{
		if ( words == null )
		{
			throw new WordListInitException (
					"Word list cannot be initialized." );
		}
		if ( words.get ( 0 ).matches ( STATIC_FIELD )
				&& isFieldHeader ( words.get ( 1 ) )
				&& words.get ( 2 ).equals ( ":" )
				&& isReturnType ( words.get ( 3 ) ) )
		{
			return true;
		}

		return false;
	}

	private void samplingInvokingStaticFunction ( ArrayList < String > words )
	{
		// TODO Auto-generated method stub

	}

	private void samplingInvokingConstructor ( ArrayList < String > words )
	{
		// TODO Auto-generated method stub

	}

	private void samplingInvokingPublicFunction ( ArrayList < String > words )
	{
		// TODO Auto-generated method stub

	}

	private void samplingStaticFunction ( ArrayList < String > words )
	{
		// TODO Auto-generated method stub

	}

	private void samplingConstructor ( ArrayList < String > words )
	{
		// TODO Auto-generated method stub

	}

	private void samplingPublicField ( ArrayList < String > words )
	{
		// TODO Auto-generated method stub

	}

	private void samplingPublicFunction ( ArrayList < String > words )
	{
		// TODO Auto-generated method stub

	}

	private boolean isInvokingStaticFunction ( ArrayList < String > words )
	{
		// TODO Auto-generated method stub
		return false;
	}

	private boolean isInvokingConstructor ( ArrayList < String > words )
	{
		// TODO Auto-generated method stub
		return false;
	}

	private boolean isInvokingPublicField ( ArrayList < String > words )
	{
		// TODO Auto-generated method stub
		return false;
	}

	private boolean isInvokingPublicFunction ( ArrayList < String > words )
	{
		// TODO Auto-generated method stub
		return false;
	}

	private boolean isStaticFunction ( ArrayList < String > words )
			throws WordListInitException
	{
		if ( words == null )
		{
			throw new WordListInitException (
					"Word list cannot be initialized." );
		}
		if ( words.get ( 0 ).equals ( "public" )
				&& ( words.get ( 1 ).equals ( "static" ) && words.get ( 2 )
						.matches (
								ID_NAME + OPEN_BRACKET + PARAM_TYPE_DESC
										+ CLOSE_BRACKET + RETURN_TYPE_DESC ) ) )
		{
			return true;
		}

		return false;
	}

	private boolean isMemberConstructor ( ArrayList < String > words )
			throws WordListInitException
	{
		if ( words == null )
		{
			throw new WordListInitException (
					"Word list cannot be initialized." );
		}
		if ( words.get ( 0 ).equals ( "public" )
				&& ( words.get ( 1 ).matches (
						CONSTRUCTOR_NAME + OPEN_BRACKET + CLOSE_BRACKET + "V" ) || words
						.get ( 1 )
						.matches (
								CONSTRUCTOR_NAME + OPEN_BRACKET
										+ PARAM_TYPE_DESC + CLOSE_BRACKET + "V" ) ) )
		{
			return true;
		}

		return false;
	}

	private boolean isMemberPublicField ( ArrayList < String > words )
			throws WordListInitException
	{
		if ( words == null )
		{
			throw new WordListInitException (
					"Word list cannot be initialized." );
		}
		if ( ( words.get ( 0 ).equals ( "PUTFIELD" ) || words.get ( 0 ).equals (
				"GETFIELD" ) )
				&& isFieldHeader ( words.get ( 1 ) )
				&& words.get ( 2 ).equals ( ":" )
				&& isReturnType ( words.get ( 3 ) ) )
		{
			return true;
		}

		return false;
	}

	private boolean isReturnType ( String string )
	{
		String regex = RETURN_TYPE_DESC;
		return string.matches ( regex );
	}

	private boolean isFieldHeader ( String string )
	{
		String regex = PACKAGE_NAME + ID_NAME + "." + ID_NAME;
		return string.matches ( regex );
	}

	private boolean isMemberPublicFunction ( ArrayList < String > words )
			throws WordListInitException
	{
		if ( words == null )
		{
			throw new WordListInitException (
					"Word list cannot be initialized." );
		}
		if ( words.get ( 0 ).equals ( "public" )
				&& isFunctionHeader ( words.get ( 1 ) ) )
		{
			return true;
		}

		return false;
	}

	private boolean isFunctionHeader ( String header )
	{
		String regex = FUNCTION_NAME + OPEN_BRACKET + PARAM_TYPE_DESC
				+ CLOSE_BRACKET + RETURN_TYPE_DESC;
		return header.matches ( regex );
	}

	private void fillup ( ArrayList < String > words , StringTokenizer st )
	{
		if ( st == null )
		{
			return;
		}
		while ( st.hasMoreTokens ( ) )
		{
			words.add ( st.nextToken ( ) );
		}
	}
}
